/**
* Group: Y1
* The GameManager manages all the components in Gameplay which include ships, bullets, collision, and networking
* @author Mason Kwait
* @author Venkat Patnala
*/

package lightspeed.Backend;

import lightspeed.Network.*;
import java.util.ArrayList;

import lightspeed.Media.Graphics.WindowManager;

public class GameManager extends Thread
{
	
	public static final int GAME_SPEED = 50;
	
	public static int winScore = 5;
	
	private LightServer server;
	private CollisionManager collideMan;
	
	private ArrayList bullets;
	private ArrayList ships;
	
	public static short scoreBlue, scoreRed;
	public static Flag flagBlue, flagRed;
	
	private float[][] velocities; // x,y
	/**
	* constructor: creates an instance of GameManager
	* @param ls A reference to the server that creates the GameManager
	*/
	public GameManager(LightServer ls)
	{
		server = ls;
		
		bullets = new ArrayList();
		ships = new ArrayList();
		
		scoreRed = 0;
		scoreBlue = 0;
		
		collideMan = new CollisionManager(ships, bullets);
		
		velocities = new float[36][2];
		
		for(int i = 0; i < 36; ++i)
		{
			velocities[i][0] = (float)Math.cos(Math.toRadians(i*10)); // get x velocity unit
			
			velocities[i][1] = (float)Math.sin(Math.toRadians(i*10)); // get y velocity unit
			
			//System.out.println(velocities[i][0] + ", " + velocities[i][1]);
		}
		
		flagBlue = new Flag(0, 100, (int)(100 + 2800*Math.random())); //initialize flags
		flagRed = new Flag(1, 2900, (int)(100 + 2800*Math.random()));
	}
    /**
	* Updates the game in terms of ships, bullets, and collisions that take place.
	*/
	public void updateGame()
	{
		//System.out.println("SCORES: BLUE - "+scoreBlue+":: RED - "+scoreRed);
//		System.out.println("Player Statuses:");

		//check for a win
		if(scoreRed == winScore || scoreBlue == winScore)
		{
			LightServer.spacket.gameOver = 1;
			server.resetGame();
		}
		else
		{
			if(ships.size() != 0)
			LightServer.spacket.gameOver = 0;
		}
		//
		
		for(int i = 0; i < LightServer.cpackets.length; ++i)
		{
			if(LightServer.cpackets[i] != null)
			{
//				System.out.print("Player" + i + "- left: " + LightServer.cpackets[i].leftDown + " right: " + LightServer.cpackets[i].rightDown + " up: " + LightServer.cpackets[i].upDown + " fire: " + LightServer.cpackets[i].spaceDown + " ");
				//System.out.println("Packet " + i + "recieved");
				
				if(ships.size() == i) // check if player is not added
				{
					float sX, sY;
					float modX = (float)Math.random()*100;
					float modY = (float)Math.random()*100;
					if((((int)(Math.random()*2)) % 2) == 0)
						modX*=-1;
					if((((int)(Math.random()*2)) % 2) == 0)
						modY*=-1;
					if(((int)(i%2) & 1) == 0) //blue team
					{
						sX = (float) (GameManager.flagBlue.startX + modX);
						sY = (float) (GameManager.flagBlue.startY + modY);
					}
					else //red team
					{
						sX = (float) (GameManager.flagRed.startX + modX);
						sY = (float) (GameManager.flagRed.startY + modY);
					}
					Ship tba = new Ship(sX, sY, (short)(i%2));
					ships.add(tba);
					//collideMan.addShip(tba);
					
					//System.out.println(" - after size: " + ships.size());
				}
				
				//update bullets on first loop through
				if(i == 0)
				{
					collideMan.updateBullets();
				}
				//
				
				updateMovement(i); //updates movement information for ith ship
				
				Ship curr = (Ship)ships.get(i);
				//System.out.println("angle sent for ship " + i + ": " + curr.getAngle());
				LightServer.spacket.shipPositions[i][0] = (short)curr.getX();
				LightServer.spacket.shipPositions[i][1] = (short)curr.getY();
				LightServer.spacket.shipPositions[i][2] = (short)curr.getAngle();
				
				short dat = (short)curr.team;
				if(curr.dead)
				{
					//System.out.println("dead");
					dat = (short)((int)dat | 2); //store dead boolean in with team boolean
				}
				LightServer.spacket.shipPositions[i][3] = dat;
				//System.out.println("angle sent for ship " + i + ": " + curr.getAngle());
				
			}
		}
		
		//update flag movement
		flagBlue.updatePosition();
		flagRed.updatePosition();
		LightServer.spacket.scores[0] = (short)scoreBlue;
		LightServer.spacket.scores[1] = (short)scoreRed;
		//
		
		if(bullets.size() >= 1 && bullets.get(0) != null && bullets.get(0) instanceof Bullet)
		{
			System.out.println("first bullet speed: " + ((Bullet)(bullets.get(0))).getVx() + ", " + ((Bullet)(bullets.get(0))).getVy());
		}
				
		LightServer.spacket.bulletPositions = new short[LightServer.numCon*LightServer.spacket.maxBulletsPerClient][3];
		for(int i = 0; i < bullets.size(); ++i)
		{
			LightServer.spacket.bulletPositions[i][0] = (short)((Bullet)bullets.get(i)).getX();
			LightServer.spacket.bulletPositions[i][1] = (short)((Bullet)bullets.get(i)).getY();
			LightServer.spacket.bulletPositions[i][2] = (short)((Bullet)bullets.get(i)).team;
		}
		
		LightServer.spacket.blueFlagX = (short)flagBlue.x;
		LightServer.spacket.blueFlagY = (short)flagBlue.y;
		LightServer.spacket.redFlagX = (short)flagRed.x;
		LightServer.spacket.redFlagY = (short)flagRed.y;
	}
	
    /**
	* Updates the movement of a ship. Checks for acceleration, rotation, and firing.
	* @param ship The ship whose movement will be upadated when the method is invoked.
	*/
	public void updateMovement(int ship)
	{
		Ship current = (Ship)ships.get(ship);
		
		//System.out.println("mark 1");
		
		if(LightServer.cpackets[ship].upDown && !current.dead) // check if accelerating
		{
			//calculate change in velocity
			float accelModifier = (float)1.6;
				
			
			/*if(false)//if(current.vVector >= Ship.MAX_VELOCITY)
			{
				System.out.println("MAX VELOCITY REACHED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				current.setVelocity((float)(Ship.MAX_VELOCITY*velocities[current.getAngle()][0]), (float)(Ship.MAX_VELOCITY*velocities[current.getAngle()][1]));
			}*/
			current.setVelocity((float)(current.getVx()+accelModifier*velocities[current.getAngle()][0]), (float)(current.getVy()+accelModifier*velocities[current.getAngle()][1]));

			float curr_velocity = (float)Math.sqrt(current.getVx()*current.getVx()+current.getVy()*current.getVy());
			
			if(curr_velocity > Ship.MAX_VELOCITY)
			{
				double angle = Math.atan(((double)(current.getVy()))/((double)(current.getVx())));
				
				double newX = Ship.MAX_VELOCITY*Math.cos(angle);
				double newY = Ship.MAX_VELOCITY*Math.sin(angle);
				
				if((current.getVx() < 0 && newX > 0) || (current.getVx() > 0 && newX < 0))
				{
					newX *= -1;
				}
				if((current.getVy() < 0 && newY > 0) || (current.getVy() > 0 && newY < 0))
				{
					newY *= -1;
				}
				
				current.setVelocity((float)newX, (float)newY);
			}
			
			//current.setVelocity((float)(Ship.MAX_VELOCITY*velocities[current.getAngle()][0]), (float)(Ship.MAX_VELOCITY*velocities[current.getAngle()][1]));
			//System.out.println("MAX VELOCITY REACHED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

						
		}
		current.updatePosition();
		Ship destroyed = collideMan.update();
		if(destroyed == current)
		{
			//current.reset();
			System.out.println("SHIP DESTROYED");
		}
		
		if(LightServer.cpackets[ship].leftDown && !current.dead) // rotating counter clockwise
		{
			//System.out.println("recieved left turn signal for ship " + ship);
			current.setAngle(current.getAngle()+1);
			if(current.getAngle() == 36)
			{
				current.setAngle(0);
				//System.out.println("reset angle");
			}
			//System.out.println("current angle: " + current.getAngle());
		}
		
		if(LightServer.cpackets[ship].rightDown && !current.dead) // rotating clockwise
		{
			//System.out.println("recieved right turn signal for ship " + ship);
			current.setAngle(current.getAngle()-1);
			if(current.getAngle() == -1)
				current.setAngle(35);
		}
		
		if(LightServer.cpackets[ship].spaceDown && !current.dead) // firing
		{
			//current.dead = true; //testing
			//System.out.println("using ship angle " + current.getAngle() + "0");
			//System.out.println("using angle modifiers " + velocities[current.getAngle()][0] + ", " + velocities[current.getAngle()][1]);
			//System.out.println("sending initial velocities " + (current.getVx() + 5*velocities[current.getAngle()][0]) + ", " + (current.getVy() + 5*velocities[current.getAngle()][1]));
			//System.out.println("ship velocity at bullet fire: " + current.getVx() + ", " + current.getVy());
			//System.out.println("fired speed: " + (double)(current.getVx() + 5*velocities[current.getAngle()][0]) + ", " + (double)(current.getVy() + 5*velocities[current.getAngle()][1]));
			bullets.add(new Bullet((current.getX() + (CollisionManager.SHIP_RANGE + 5)*velocities[current.getAngle()][0]), (current.getY() + (CollisionManager.SHIP_RANGE - 3)*velocities[current.getAngle()][1]), (short)current.team, (double)(current.getVx() + 30*velocities[current.getAngle()][0]), (double)(current.getVy() + 30*velocities[current.getAngle()][1])));
		}
		
	}

	public void run()
	{
		int upWait = GAME_SPEED;
		
		while(true)
		{
			int t = (int)System.currentTimeMillis(); //get current time
			
			updateGame();
			
			//System.out.println("gameupdate lag: " + ((int)System.currentTimeMillis() - t)); //for testing
			if((int)System.currentTimeMillis() - t > upWait) //check if repaint took longer than frameSpeed
			{
				upWait = 5; //minimum wait
			}
			else
			{
				upWait = upWait - ((int)System.currentTimeMillis() - t); //subtract time difference from frameSpeed (wait time until next frame)
			}
				
			try
			{
				Thread.sleep(upWait); // wait 50 milliseconds between updates
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			
			upWait = GAME_SPEED;
		}
	}
}
